using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using Game500.ClientServerCommon.Src;
using Game500.ClientServerCommon.Src.Networking;
using Microsoft.Xna.Framework;
using ProtoBuf;
using ProtoBuf.Meta;

namespace Game500.Client.Src.Networking
{
  internal class TcpClientNetworkManager : ClientNetworkManager
  {
    private readonly TcpClient myTcpClient = new TcpClient(AddressFamily.InterNetwork);
    private SceneFrame myActualFrame;
    private GameInfo myGameInfo;
    private NetworkPacketStream myNetworkPacketStream;

    static TcpClientNetworkManager()
    {
      RuntimeTypeModel.Default.Add(typeof (Vector3), true).Add("X", "Y", "Z");
    }

    public override GameInfo GameInfo
    {
      get { return myGameInfo; }
    }

    public override void Connect(string host, int port)
    {
      myTcpClient.Connect(host, port);
      myNetworkPacketStream = new NetworkPacketStream(myTcpClient.GetStream());
      myNetworkPacketStream.StartListen();
      myNetworkPacketStream.DataReceivedEvent += DataReceivedHandler;
      myNetworkPacketStream.ErrorEvent += ErrorHandler;
    }

    private void ErrorHandler(Exception exception)
    {
      throw exception;
    }

    private void DataReceivedHandler(NetworkPacket packet)
    {
      using (var stream = new MemoryStream(packet.InnerData))
      {
        switch (packet.Type)
        {
          case NetworkPacketType.SceneFramePacket:
            myActualFrame = Serializer.Deserialize<SceneFrame>(stream);
            break;
          case NetworkPacketType.GameInfoPacket:
            myGameInfo = Serializer.Deserialize<GameInfo>(stream);
            RaiseGameStartEvent();
            break;
          case NetworkPacketType.ExplosionEventPacket:
            var expEv = Serializer.Deserialize<ExplosionsEvent>(stream);
            RaiseExplosionsEvent(expEv);
            break;
          case NetworkPacketType.ShotEventPacket:
            var shotsEv = Serializer.Deserialize<ShotsEvent>(stream);
            RaiseShotsEvent(shotsEv);
            break;
          case NetworkPacketType.PlayerLimitReachedPacket:
            var limitEv = Serializer.Deserialize<PlayerLimitReachedEvent>(stream);
            RaisePlayerLimitReachedEvent(limitEv);
            break;
        }
      }
    }

    #region Overrides of ClientNetworkManager

    public override SceneFrame GetActualFrame()
    {
      return myActualFrame;
    }

    public override void Initialize()
    {
    }

    public override void Update()
    {
//			foreach (var action in Actions)
//			{
//				SendAction(action.Value);
//				Thread.Sleep(5);
//			}
//			Actions.Clear();
      SendAllActions();
    }

    public override void SendGamerInfo(GamerInfo gamerInfo)
    {
      using (var stream = new MemoryStream())
      {
        Serializer.Serialize(stream, gamerInfo);
        NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.GamerInfoPacket, stream.GetBuffer(), (int)stream.Length);
        myNetworkPacketStream.SendPacket(packet);
      }
    }

    private void SendAllActions()
    {
      IList<NetworkPacket> packets = new List<NetworkPacket>();
      foreach (var action in Actions)
      {
        using (var stream = new MemoryStream())
        {
          Serializer.Serialize(stream, action.Value);
          NetworkPacket packet = NetworkPacket.CreatePacket(NetworkPacketType.GamerActionPacket, stream.GetBuffer(), (int)stream.Length);
          packets.Add(packet);
        }
      }
      myNetworkPacketStream.TrySendMultipleNetworkPackets(packets);
      Actions.Clear();
    }

    #endregion
  }
}